//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// <regex>

// template <class BidirectionalIterator> class sub_match;

// Note in C++20 several of these operators have been removed and implicitly
// generated by the compiler using operator== and operator<=>.

// template <class BiIter>
//     bool
//     operator==(const sub_match<BiIter>& lhs, const sub_match<BiIter>& rhs);
//
// template <class BiIter>
//     bool
//     operator!=(const sub_match<BiIter>& lhs, const sub_match<BiIter>& rhs);
//
// template <class BiIter>
//     bool
//     operator<(const sub_match<BiIter>& lhs, const sub_match<BiIter>& rhs);
//
// template <class BiIter>
//     bool
//     operator<=(const sub_match<BiIter>& lhs, const sub_match<BiIter>& rhs);
//
// template <class BiIter>
//     bool
//     operator>=(const sub_match<BiIter>& lhs, const sub_match<BiIter>& rhs);
//
// template <class BiIter>
//     bool
//     operator>(const sub_match<BiIter>& lhs, const sub_match<BiIter>& rhs);
//
// template <class BiIter, class ST, class SA>
//     bool
//     operator==(const basic_string<typename iterator_traits<BiIter>::value_type, ST, SA>& lhs,
//                const sub_match<BiIter>& rhs);
//
// template <class BiIter, class ST, class SA>
//     bool
//     operator!=(const basic_string<typename iterator_traits<BiIter>::value_type, ST, SA>& lhs,
//                const sub_match<BiIter>& rhs);
//
// template <class BiIter, class ST, class SA>
//     bool
//     operator<(const basic_string<typename iterator_traits<BiIter>::value_type, ST, SA>& lhs,
//               const sub_match<BiIter>& rhs);
//
// template <class BiIter, class ST, class SA>
//     bool
//     operator>(const basic_string<typename iterator_traits<BiIter>::value_type, ST, SA>& lhs,
//               const sub_match<BiIter>& rhs);
//
// template <class BiIter, class ST, class SA>
//     bool operator>=(const basic_string<typename iterator_traits<BiIter>::value_type, ST, SA>& lhs,
//                     const sub_match<BiIter>& rhs);
//
// template <class BiIter, class ST, class SA>
//     bool
//     operator<=(const basic_string<typename iterator_traits<BiIter>::value_type, ST, SA>& lhs,
//                const sub_match<BiIter>& rhs);
//
// template <class BiIter, class ST, class SA>
//     bool
//     operator==(const sub_match<BiIter>& lhs,
//                const basic_string<typename iterator_traits<BiIter>::value_type, ST, SA>& rhs);
//
// template <class BiIter, class ST, class SA>
//     bool
//     operator!=(const sub_match<BiIter>& lhs,
//                const basic_string<typename iterator_traits<BiIter>::value_type, ST, SA>& rhs);
//
// template <class BiIter, class ST, class SA>
//     bool
//     operator<(const sub_match<BiIter>& lhs,
//               const basic_string<typename iterator_traits<BiIter>::value_type, ST, SA>& rhs);
//
// template <class BiIter, class ST, class SA>
//     bool operator>(const sub_match<BiIter>& lhs,
//                    const basic_string<typename iterator_traits<BiIter>::value_type, ST, SA>& rhs);
//
// template <class BiIter, class ST, class SA>
//     bool
//     operator>=(const sub_match<BiIter>& lhs,
//                const basic_string<typename iterator_traits<BiIter>::value_type, ST, SA>& rhs);
//
// template <class BiIter, class ST, class SA>
//     bool
//     operator<=(const sub_match<BiIter>& lhs,
//                const basic_string<typename iterator_traits<BiIter>::value_type, ST, SA>& rhs);
//
// template <class BiIter>
//     bool
//     operator==(typename iterator_traits<BiIter>::value_type const* lhs,
//                const sub_match<BiIter>& rhs);
//
// template <class BiIter>
//     bool
//     operator!=(typename iterator_traits<BiIter>::value_type const* lhs,
//                const sub_match<BiIter>& rhs);
//
// template <class BiIter>
//     bool
//     operator<(typename iterator_traits<BiIter>::value_type const* lhs,
//               const sub_match<BiIter>& rhs);
//
// template <class BiIter>
//     bool
//     operator>(typename iterator_traits<BiIter>::value_type const* lhs,
//               const sub_match<BiIter>& rhs);
//
// template <class BiIter>
//     bool
//     operator>=(typename iterator_traits<BiIter>::value_type const* lhs,
//                const sub_match<BiIter>& rhs);
//
// template <class BiIter>
//     bool
//     operator<=(typename iterator_traits<BiIter>::value_type const* lhs,
//                const sub_match<BiIter>& rhs);
//
// template <class BiIter>
//     bool
//     operator==(const sub_match<BiIter>& lhs,
//                typename iterator_traits<BiIter>::value_type const* rhs);
//
// template <class BiIter>
//     bool
//     operator!=(const sub_match<BiIter>& lhs,
//                typename iterator_traits<BiIter>::value_type const* rhs);
//
// template <class BiIter>
//     bool
//     operator<(const sub_match<BiIter>& lhs,
//               typename iterator_traits<BiIter>::value_type const* rhs);
//
// template <class BiIter>
//     bool
//     operator>(const sub_match<BiIter>& lhs,
//               typename iterator_traits<BiIter>::value_type const* rhs);
//
// template <class BiIter>
//     bool
//     operator>=(const sub_match<BiIter>& lhs,
//                typename iterator_traits<BiIter>::value_type const* rhs);
//
// template <class BiIter>
//     bool
//     operator<=(const sub_match<BiIter>& lhs,
//                typename iterator_traits<BiIter>::value_type const* rhs);
//
// template <class BiIter>
//     bool
//     operator==(typename iterator_traits<BiIter>::value_type const& lhs,
//                const sub_match<BiIter>& rhs);
//
// template <class BiIter>
//     bool
//     operator!=(typename iterator_traits<BiIter>::value_type const& lhs,
//                const sub_match<BiIter>& rhs);
//
// template <class BiIter>
//     bool
//     operator<(typename iterator_traits<BiIter>::value_type const& lhs,
//               const sub_match<BiIter>& rhs);
//
// template <class BiIter>
//     bool
//     operator>(typename iterator_traits<BiIter>::value_type const& lhs,
//               const sub_match<BiIter>& rhs);
//
// template <class BiIter>
//     bool
//     operator>=(typename iterator_traits<BiIter>::value_type const& lhs,
//                const sub_match<BiIter>& rhs);
//
// template <class BiIter>
//     bool
//     operator<=(typename iterator_traits<BiIter>::value_type const& lhs,
//                const sub_match<BiIter>& rhs);
//
// template <class BiIter>
//     bool
//     operator==(const sub_match<BiIter>& lhs,
//                typename iterator_traits<BiIter>::value_type const& rhs);
//
// template <class BiIter>
//     bool
//     operator!=(const sub_match<BiIter>& lhs,
//                typename iterator_traits<BiIter>::value_type const& rhs);
//
// template <class BiIter>
//     bool
//     operator<(const sub_match<BiIter>& lhs,
//               typename iterator_traits<BiIter>::value_type const& rhs);
//
// template <class BiIter>
//     bool
//     operator>(const sub_match<BiIter>& lhs,
//               typename iterator_traits<BiIter>::value_type const& rhs);
//
// template <class BiIter>
//     bool
//     operator>=(const sub_match<BiIter>& lhs,
//                typename iterator_traits<BiIter>::value_type const& rhs);
//
// template <class BiIter>
//     bool
//     operator<=(const sub_match<BiIter>& lhs,
//                typename iterator_traits<BiIter>::value_type const& rhs);

// Added in C++20
// template <class BiIter>
//     auto
//     operator<=>(const sub_match<BiIter>& lhs, const sub_match<BiIter>& rhs);
// template <class BiIter, class ST, class SA>
//     auto
//     operator<=>(const sub_match<BiIter>& lhs,
//                 const basic_string<typename iterator_traits<BiIter>::value_type, ST, SA>& rhs);
//
// template <class BiIter>
//     auto
//     operator<=>(const sub_match<BiIter>& lhs,
//                 typename iterator_traits<BiIter>::value_type const* rhs);
//
// template <class BiIter>
//     auto
//     operator<=>(const sub_match<BiIter>& lhs,
//                 typename iterator_traits<BiIter>::value_type const& rhs);

#include <regex>
#include <array>
#include <cassert>

#include "constexpr_char_traits.h"
#include "make_string.h"
#include "test_comparisons.h"
#include "test_macros.h"


#define SV(S) MAKE_STRING_VIEW(CharT, S)

template <class CharT>
void
test(const std::basic_string<CharT>& x, const std::basic_string<CharT>& y, bool doCStrTests = true)
{
    typedef std::basic_string<CharT> string;
    typedef std::sub_match<typename string::const_iterator> sub_match;
#if TEST_STD_VER > 17
    AssertOrderReturn<std::strong_ordering, sub_match>();
    AssertOrderReturn<std::strong_ordering, sub_match, string>();
#else
    AssertComparisonsReturnBool<sub_match>();
    AssertComparisonsReturnBool<sub_match, string>();
#endif
    sub_match sm1;
    sm1.first = x.begin();
    sm1.second = x.end();
    sm1.matched = true;
    sub_match sm2;
    sm2.first = y.begin();
    sm2.second = y.end();
    sm2.matched = true;

    assert(testComparisons(sm1, sm2, x == y, x < y));
    assert(testComparisons(x, sm2, x == y, x < y));
    assert(testComparisons(sm1, y, x == y, x < y));
#if TEST_STD_VER > 17
    assert(testOrder(sm1, sm2, x <=> y));
    assert(testOrder(x, sm2, x <=> y));
    assert(testOrder(sm1, y, x <=> y));
#endif

    if (doCStrTests) {
        assert(testComparisons(x.c_str(), sm2, x == y, x < y));
        assert(testComparisons(sm1, y.c_str(), x == y, x < y));
#if TEST_STD_VER > 17
        assert(testOrder(x.c_str(), sm2, x <=> y));
        assert(testOrder(sm1, y.c_str(), x <=> y));
#endif
    }

    assert(testComparisons(x[0], sm2, string(1, x[0]) == y, string(1, x[0]) < y));
    assert(testComparisons(sm1, y[0], x == string(1, y[0]), x < string(1, y[0])));
#if TEST_STD_VER > 17
    assert(testOrder(x[0], sm2, (string(1, x[0]) <=> y)));
    assert(testOrder(sm1, y[0], x <=> (string(1, y[0]))));
#endif
}

#if TEST_STD_VER > 17
template <class CharT, class Ordering>
struct char_traits : public constexpr_char_traits<CharT> {
    using comparison_category = Ordering;
};

template <class T, class Ordering = std::strong_ordering>
constexpr void test() {
  AssertOrderAreNoexcept<T>();
  AssertOrderReturn<Ordering, T>();

  using CharT = typename T::value_type;

  // sorted values
  std::array s = [] {
    std::array input{SV(""), SV("abc"), SV("abcdef")};
    return std::array{
        T{input[0].begin(), input[0].end()}, T{input[1].begin(), input[1].end()}, T{input[2].begin(), input[2].end()}};
  }();
  auto ctor = [](const T& string) {
    std::sub_match<typename T::const_iterator> sm;
    sm.first   = string.begin();
    sm.second  = string.end();
    sm.matched = true;
    return sm;
  };
  std::array sm{ctor(s[0]), ctor(s[1]), ctor(s[2])};

  for (std::size_t i = 0; i < s.size(); ++i) {
    for (std::size_t j = 0; j < s.size(); ++j) {
      assert(testOrder(s[i], sm[j], i == j ? Ordering::equivalent : i < j ? Ordering::less : Ordering::greater));
    }
  }
}

template <class CharT>
constexpr void test_all_orderings() {
  test<std::basic_string<CharT>>(); // Strong ordering in its char_traits
  test<std::basic_string<CharT, constexpr_char_traits<CharT>>,
       std::weak_ordering>(); // No ordering in its char_traits
  test<std::basic_string<CharT, char_traits<CharT, std::weak_ordering>>, std::weak_ordering>();
  test<std::basic_string<CharT, char_traits<CharT, std::partial_ordering>>, std::partial_ordering>();
}
#endif //  TEST_STD_VER > 17

int main(int, char**)
{
    test(std::string("123"), std::string("123"));
    test(std::string("1234"), std::string("123"));
    test(std::string("123\000" "56", 6), std::string("123\000" "56", 6), false);
#if TEST_STD_VER > 17
    test_all_orderings<char>();
#endif

#ifndef TEST_HAS_NO_WIDE_CHARACTERS
    test(std::wstring(L"123"), std::wstring(L"123"));
    test(std::wstring(L"1234"), std::wstring(L"123"));
    test(std::wstring(L"123\000" L"56", 6), std::wstring(L"123\000" L"56", 6), false);
#if TEST_STD_VER > 17
    test_all_orderings<wchar_t>();
#endif
#endif // TEST_HAS_NO_WIDE_CHARACTERS

  return 0;
}
